package edu.washington.cs.sounddetector.server.api;

/**
 * <p>
 * A MethodResult represents the response from executing a {@link Method}. A MethodResult will
 * provide a method call to obtain the results of the Method. This method will either return the
 * results of the Method or throw a subclass of {@link SoundDetectorAPIException} to indicate
 * failure.
 * </p>
 * 
 * <p>
 * More details:
 * </p>
 * 
 * <p>
 * There are two general states a MethodResult can be in: one for if the Method was successful and
 * one for if it was not successful.
 * </p>
 * 
 * <p>
 * If the execution of a Method was successful, then the MethodResult will contain the expected data
 * from running the Method.
 * </p>
 * 
 * <p>
 * On the other hand, if the execution of a Method was unsuccessful, then the MethodResult will
 * contain the reason it was unsuccessful.
 * </p>
 * 
 * <p>
 * Requirements for subclasses of MethodResult:
 * <ul>
 * <li>A private all-arg constructor.</li>
 * </ul>
 * <li>A getter method which returns the data of the result, or the data of the failure.</li>
 * <li>A static 'successful' method which creates and returns an instance of your class.</li>
 * <li>One or more static 'failure' methods which create and resturn an instance of your class.</li>
 * </ul>
 * 
 * @author the.dylan.price@gmail.com
 */
public abstract class MethodResult<T extends Method>
{
    /**
     * FailureReason enumerates all the possible reasons the execution of a method could fail.
     */
    public enum FailureReason
    {
        CANT_BUILD_CLASSIFIER,
        PHONE_DOES_NOT_EXIST,
        DETECTOR_ID_DOES_NOT_EXIST,
        SOUND_ID_DOES_NOT_EXIST,
        SOUND_CLASS_DOES_NOT_EXIST,
    }

    private boolean successful;
    private FailureReason reason;

    /**
     * Constructs a new MethodResult. All subclasses should call this constructor.
     * 
     * @param successful
     *            whether the method was successful or not
     * @param reason
     *            If successful == true, then this should be null. If successful == false, then this
     *            must not be null.
     */
    protected MethodResult(boolean successful, FailureReason reason)
    {
        if (successful && reason != null)
        {
            throw new IllegalArgumentException(
                    "Can not be successful and have a non-null FailureReason");
        }
        if (!successful && reason == null)
        {
            throw new IllegalArgumentException(
                    "Can not be unsuccessful and have a null FailureReason");
        }

        this.successful = successful;
        this.reason = reason;
    }

    /**
     * @return the successful
     */
    public boolean isSuccessful()
    {
        return successful;
    }

    /**
     * @param successful
     *            the successful to set
     */
    public void setSuccessful(boolean successful)
    {
        this.successful = successful;
    }

    /**
     * @param reason
     *            the reason to set
     */
    public void setReason(FailureReason reason)
    {
        this.reason = reason;
    }

    /**
     * @return if the method failed, then returns the reason it failed. Otherwise returns null.
     */
    public FailureReason getReason()
    {
        if (!isSuccessful())
            return this.reason;
        else
            return null;
    }

    @Override
    public String toString()
    {
        return String.format("Success = %s, Reason = %s", this.successful, this.reason);
    }
}
